package yunjiao.javatutorials.guava.concurrent;

import com.google.common.util.concurrent.Striped;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;

/**
 * 创建细粒度锁的工具，适用于需要根据特定键（如 ID、名称等）进行同步的场景
 *
 * @author yangyunjiao
 */
public class StripedLockDemo {

    private static final ConcurrentHashMap<Long, Account> ACCOUNTS = new ConcurrentHashMap<>();

    static class Account {
        private final Long id;

        private final Striped<Lock> STRIPED_LOCK;

        private double balance;

        public Account(Long id, Striped<Lock> STRIPED_LOCK, double balance) {
            this.id = id;
            this.STRIPED_LOCK = STRIPED_LOCK;
            this.balance = balance;
        }

        public void transfer(Account to, double amount) {
            // 获取两个账户的锁，避免死锁（按固定顺序获取）
            Lock firstLock = STRIPED_LOCK.get(Math.min(this.id, to.id));
            Lock secondLock = STRIPED_LOCK.get(Math.max(this.id, to.id));

            firstLock.lock();
            try {
                secondLock.lock();
                try {
                    if (this.balance >= amount) {
                        this.balance -= amount;
                        to.balance += amount;
                        System.out.printf("转账成功: %d -> %d, 金额: %.2f%n",
                                this.id, to.id, amount);
                    } else {
                        System.out.printf("余额不足: %d -> %d, 金额: %.2f%n",
                                this.id, to.id, amount);
                    }
                } finally {
                    secondLock.unlock();
                }
            } finally {
                firstLock.unlock();
            }
        }

        public double getBalance() {
            Lock lock = STRIPED_LOCK.get(this.id);
            lock.lock();
            try {
                return balance;
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        // 创建包含5个锁条带的Striped锁: 同时允许5个用户转账
        final Striped<Lock> STRIPED_LOCK = Striped.lock(5);

        // 初始化账户
        for (long i = 1; i <= 5; i++) {
            ACCOUNTS.put(i, new Account(i, STRIPED_LOCK, 1000.0));
        }

        int threadNum = 17;
        ExecutorService executor = Executors.newFixedThreadPool(10);
        CountDownLatch latch = new CountDownLatch(threadNum);

        // 模拟并发转账
        for (int i = 0; i < threadNum; i++) {
            final int index = i;
            executor.submit(() -> {
                try {
                    Long fromId = (long) (index % 5 + 1);
                    Long toId = (long) ((index + 1) % 5 + 1);
                    Account from = ACCOUNTS.get(fromId);
                    Account to = ACCOUNTS.get(toId);

                    from.transfer(to, 50.0);
                } finally {
                    latch.countDown();
                }
            });
        }

        latch.await();
        executor.shutdown();

        // 打印最终余额
        System.out.println("\n最终账户余额:");
        ACCOUNTS.forEach((id, account) ->
                System.out.printf("账户 %d: %.2f%n", id, account.getBalance()));
    }
}
